package model

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

var _ FundModel = (*customFundModel)(nil)

type (
	// FundModel is an interface to be customized, add more methods here,
	// and implement the added methods in customFundModel.
	FundModel interface {
		fundModel
		FindByType(ctx context.Context, fundType string) ([]*Fund, error)
	}

	customFundModel struct {
		*defaultFundModel
	}

	fundModel interface {
		Insert(ctx context.Context, data *Fund) (sql.Result, error)
		FindOne(ctx context.Context, id string) (*Fund, error)
		Update(ctx context.Context, data *Fund) error
		Delete(ctx context.Context, id string) error
		FindAll(ctx context.Context, page, pageSize int64) ([]*Fund, int64, error)
	}

	defaultFundModel struct {
		conn  sqlx.SqlConn
		table string
	}

	Fund struct {
		Id        string    `db:"id"`
		Name      string    `db:"name"`
		Code      string    `db:"code"`
		Type      string    `db:"type"`
		Nav       float64   `db:"nav"`
		UpdatedAt time.Time `db:"updated_at"`
	}
)

// NewFundModel returns a model for the database table.
func NewFundModel(conn sqlx.SqlConn) FundModel {
	return &customFundModel{
		defaultFundModel: newFundModel(conn),
	}
}

func newFundModel(conn sqlx.SqlConn) *defaultFundModel {
	return &defaultFundModel{
		conn:  conn,
		table: "fund",
	}
}

func (m *defaultFundModel) Insert(ctx context.Context, data *Fund) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (id, name, code, type, nav, updated_at) values (?, ?, ?, ?, ?, ?)", m.table)
	return m.conn.ExecCtx(ctx, query, data.Id, data.Name, data.Code, data.Type, data.Nav, data.UpdatedAt)
}

func (m *defaultFundModel) FindOne(ctx context.Context, id string) (*Fund, error) {
	query := fmt.Sprintf("select id, name, code, type, nav, updated_at from %s where id = ? limit 1", m.table)
	var resp Fund
	err := m.conn.QueryRowCtx(ctx, &resp, query, id)
	return &resp, err
}

func (m *defaultFundModel) Update(ctx context.Context, data *Fund) error {
	query := fmt.Sprintf("update %s set name = ?, code = ?, type = ?, nav = ?, updated_at = ? where id = ?", m.table)
	_, err := m.conn.ExecCtx(ctx, query, data.Name, data.Code, data.Type, data.Nav, data.UpdatedAt, data.Id)
	return err
}

func (m *defaultFundModel) Delete(ctx context.Context, id string) error {
	query := fmt.Sprintf("delete from %s where id = ?", m.table)
	_, err := m.conn.ExecCtx(ctx, query, id)
	return err
}

func (m *defaultFundModel) FindAll(ctx context.Context, page, pageSize int64) ([]*Fund, int64, error) {
	query := fmt.Sprintf("select id, name, code, type, nav, updated_at from %s limit ? offset ?", m.table)
	var resp []*Fund
	err := m.conn.QueryRowsCtx(ctx, &resp, query, pageSize, (page-1)*pageSize)
	if err != nil {
		return nil, 0, err
	}

	countQuery := fmt.Sprintf("select count(*) from %s", m.table)
	var count int64
	err = m.conn.QueryRowCtx(ctx, &count, countQuery)
	if err != nil {
		return nil, 0, err
	}

	return resp, count, nil
}

func (m *customFundModel) FindByType(ctx context.Context, fundType string) ([]*Fund, error) {
	query := fmt.Sprintf("select id, name, code, type, nav, updated_at from %s where type = ?", m.table)
	var resp []*Fund
	err := m.conn.QueryRowsCtx(ctx, &resp, query, fundType)
	return resp, err
}
